NOTE: This file is one of the major reasons I decided to remake AVS Primer.  It is full of holes and half of my facts are blatantly incorrect.  The only thing that I'm sure is right in here is the section on AVS's coding language, and you really shouldn't listen to closely to my commentary on that in this file.  There will be a new version out shortly and I'm only releasing this to avoid gripes from people redirecting newbies to my primer in hopes of them finding some good advice on coding.

Which is precisely what this isn't.

-----------------------------------------------











So, you wanna be an AVS wizard?  Well, it's not as easy as that.  I have heard a lot of people ask, "What's the math that Superscopes/DMs use?" and "How do I use Superscopes/DMs?"  Before you dive into this, then you need to know how to use all of the effects in AVS.  So if you don't know how everything works, check out the rest of the Primer first.  But other than that, the math for superscopes and DMs is just that, MATH.  And that's the core of the complex effects in AVS.  There's just a few other variables built around it, and a very simple programming language.  But as I said, the core is math, so here it is:


Of Numbers and Equations

The basic operators used in AVS math are addition (+), subtraction (-), multiplication (*), division (/), and modulus (%).  Modulus takes the remainder of a division; i.e., 8 % 3 = the remainder of 8 divided by 3 = 2.  Modulus can ONLY support positive integers!  So if you try to do -4 % 2.86, you'll run into trouble.
There is also a vast array of functions.  I will explain them here.  X, Y, and Z are simply arbitrary values, which can be any variable (see below for an explanation of variables).  And the explanation of the math you might not know comes after the function.

abs(x) = the absolute value of x.  The absolute value of a number is defined as it's distance from 0 on the number line - basically you change it's sign (positive or negative) to positive.
---
sin(x) = the sine of x.
cos(x) = the cosine of x.
tan(x) = the tangent of x.
TRIGONOMIC FUNCTIONS
The above three functions are trigonomic functions.  Trigonomic functions are ratios of the lengths of the sides of a right triangle (and a right triangle is a triangle where one of the angles is right).  x, in this case, is one of the angles.  We can (from x) find the lengths of the other sides (no, I don't know how, other than drawing it).  Then we can take the ratio, because that ratio stays the same no matter how big the sides are; it only depends on the angle.  Now, we refer to the adjacent, the opposite and the hypotenuse when dealing with trigonomic functions.  The adjacent is the side that contains the angle (x) and the right angle.  The opposite is the side that does not contain the angle x.  And the hypotenuse is the side that does not contain the right angle.
The sine is the length of the opposite side divided by the hypotenuse, the cosine is the adjacent side over the hypotenuse, and the tangent is the opposite over the adjacent.
asin(x) = the arcsine of x.
acos(x) = the arccosine of x.
atan(x) = the arctangent of x.
I'm not quite sure what the difference between a function and its arcfunction is.
But as a final note, all of these functions are of x expressed in radians.  One radian is 180 / pi, or approximately 57.29578.  So sin(x) is actually the sine of (x * 180 / pi)
---
sqr(x) = x squared
sqrt(x) = the square root of x.  To avoid taking the square root of a negative number, AVS basically takes the square root of the absolute value of x.
pow(x,y) = x to the yth power
---
log(x) = the logarithm, base e, of x
log10(x) = the logarithm, base 10, of x
LOGARITHMIC FUNCTIONS
A logarithm expresses the power to which a base number needs to be raised to reach a certain given number.  I.e., the logarithm, base 5, of 25 is 2, because 5 squared is 25.  e, or the Euler-Constant, is the limit (where n is the highest real number possible - not infinity, as infinity is not a real number!) of the equation (n + 1 / n) ^ n.
However, if we use very high numbers, we can find e to be approximately 2.71428.
---
sign(x) = the sign of x (positive or negative 1).
min(x,y) = the least of the set {x,y}.
max(x,y) = the greatest of the set {x,y}.
sigmoid(x,y) ... I'm not quite sure what this does.
rand(x) = a random number between x and 0, including x and 0 as possibilities
---
bor(x,y) = 1 if either x or y are not 0, and 0 otherwise
band(x,y) = 1 if both x and y are not 0, and 0 otherwise
bnot(x) = 1 if x is 0, and 0 otherwise
if(x,y,z) = y if x is not 0, and z otherwise
equal(x,y) = 1 if x is equal to y, and 0 otherwise
above(x,y) = 1 if x is greater than y, and 0 otherwise
below(x,y) = 1 if x is less than y, and 0 otherwise
LOGICAL FUNCTIONS
You'll notice that I say 'not 0' a couple of times.  This is because AVS regards ANYTHING other than 0 to be true, and only 0 to be false.  Also, this means that in bor, band, bnot, and if, the conditional statements (x and sometimes y) do not have to be other logical functions; and in equal, above, and below, x and y don't have to be variables.  For example, if you wanted to test if two conditions (let's call them a and b, and let's say that they only return 1 or 0) were both the same (not just both true, they can also be both false), you could either use a complex system of functions like this one:
bor(band(a,b),band(bnot(a),bnot(b)))
Or you could simply test if they returned equal numbers:
equal(a,b)
Which would reduce the work by about a factor of 5.
---
getosc(x,y,z) = returns the oscilloscope data from band x which has a width of y from channel z
getspec(x,y,z) = returns the spectrum analyzer data from band x which has a width of y from channel z
VOLUME DATA
Just think of the scope itself, taking vertical data from the channel z (0 is the center, 1 is the right, and 2 is the left) and going from 0 to 1 horizontally.  We want to take the data from the point x along the scope.  We can also take the average of the positions around it, with a range of y.  It took me a while to figure out, too, so don't worry.
---
There are also two other functions available in AVS that are not listed in 'expression help:'
atan2(x,y) ... The explanation I got from UnConeD on this was something like 'the arctangent of x/y, but placed in the proper quadrant.'  As an example, the variable r, used in movements, is equal to atan2(x,-y).  Past that I don't know.  However, it is useful for making circular disks and tunnels and whatnot, so keep it in mind later in this tutorial.
exp(x) ... The Euler-constant (e, or approx. 2.717281828) to the xth power (a.k.a. antiln).

--Note that the ^ operator is NOT USED in AVS; I only use it to explain exponential functions.  Instead of using something like x = x ^ 2, use x = x * x; or use x = pow(x,y) in place of x = x ^ y.


Now that you know the math, it's time to learn the one basic principle of programming:

ASSIGNMENT
You may have noticed me using equations such as x = x * x.  You know algebra; you know that you can't do this unless x = 0 or x = 1.  Well, in AVS (as in most or all programming languages), we use something called iterations.  Iterations are steps.  Basically, x = x * x is equivalent to x(n+1) = x(n) * x(n), where the 'n' or 'n+1' in parentheses is the iteration.  (No, it does not mean x * (n+1), in case you algebraic beurocrats were wondering).  So what you're doing is assigning the square of the current iteration of x to the next iteration of x.  Get it?  So when you see an equation like x = x + 1, don't run and hide, all it means is x(n+1) = x(n) + 1, and you're incrementing x by one every iteration.
So why don't we just use x(n+1) = x(n) + 1?  Because it's too cumbersome and is actually more confusing (especially to the computer) than to simply assume the iterations.
A second part of the assignment idea is which way the values go (no, I'm not off my nut).  Most people say that it's rather tortured to say that in the command x = x + 1, the value on the right gets stored in the value on the left, going 'backwards.'  Well, I say, isn't that what you do in most algebraic equations, such as y = x ^ 2 + 4 * x + 4?  The value x ^2 + 4 * x + 4 gets applied to y.  That's the same thing that's happening here; x + 1 gets stored into x.  However, you can only have one variable on the left, and it has to be a variable.


But there's one more thing to know about the language:

VARIABLES
In AVS, variables are numbers that can be absolutely anything, except for multiples of i (the square root of -1).  Their names can be of any length and can comprise of any combination of letters or numbers, but they have to start with a letter.  Their names are not case sensitive.  But there is a limit as to how many variables you can have stored in one effect.  There are a number of fixed variables in AVS:
d - the distance (in polar coordinates) = sqrt(x*x+y*y)/sqrt(2)
r - the rotation (in polar coordinates) = atan2(x,-y).  Polar coordinates are calculated by pixel; i.e., they are calculated as if the top or bottom (if the height is less than the width in pixels), or left and right sides (vice versa) equal 1 and -1 in rectangular coordinates, but points in the opposite dimension at the same number of pixels also equals 1 and -1.  So in a 300x200 window, the top and bottom equal 1 and -1 because the height is less than the width in pixels, but the left and right equal 1.333... and -1.333... because at 200 pixels to the right and left equal the same as 200 pixels at the top and bottom.  Which means that you could render a perfect square that fits perfectly within the height or width of the window if it was calculated with polar coordinates.
x - the x-coordinate of a point (in rectangular coordinates) =sin(r)*d*sqrt(2)
y - the y-coordinate of a point (in rectangular coordinates) =-cos(r)*d*sqrt(2).  Rectangular coordinates are calculated a bit more simply, the sides of the screen always equal 1 and -1, but the y-axis is upsidown - the top is -1, and the bottom is 1.  Remember this when making superscopes and movements!
n - the number of points in a superscope
i - the position along the scope, from 0 to 1.  i can be calculated as a percentage of n to m, where m is the current point being drawn: i=m/n.
v - the volume data at the point i.  Think of a regular horizontal scope: v is the y value
red, green, and blue - the rgb values, from 0 to 1; or the actual rgb values divided by 255.
alpha - the alpha blending value, from 0 to 1.  This represents how much of the previous frame is blended with the moved one; the color value in the new frame =alpha*p+(1-alpha)*m where p is the color value in the previous frame and m is the color from the moved frame.
Movements use r and d or x and y (not both sets at the same time), Dynamic Movements use those and alpha, Dynamic Shift uses alpha, Dynamic Distance Modifier uses alpha and d, and superscopes use n, i, v, x, y, red, green, and blue.  So if an effect does not used a certain fixed variable, you can use it as a custom variable.
This may seem a bit convoluted, but if you read it over a couple times you should get it.


Well, that's really all you have to know about the language itself.  Now, what good is knowing the language if you don't know what to do with it?  Here I'll show you how to write good, effective, and morally clean code.


Of Applications and Formulas

You're probably wondering, what can I do to make a good preset out of this?  First you have to understand the shape of and relationships between trigonometric functions, as they are probably the functions you'll most commonly use.

TRIGONOMETRIC WAVES

Open the file 'Math - Trigonometry 1.'  The line on top is a sine wave, and the one in the middle is a cosine wave (both calculated with the same data).  The two lines on the bottom are a cosine wave and a sine wave put together.  We'll get to the bottom lines in a minute.  First look at 'Math - Trigonometry 2.' Go into the movement and look at the code.  We're shifting d by the sine of r*5, which means that it will alternate going in and out as it goes around the circle.  And we're shifting r by the sine of d*35, which means it will rotate left or right as we go out.  These combined effects create the swirling pattern.  If you haven't caught on, you can use sines to smoothly alternate between two options.
Now go back to 'Math - Trigonometry 1' and check out the bottom lines.  Yeah, so, what can I do with this?  Notice how they match up.  When the sine wave is on the top or bottom, the cosine is in the middle, and vice versa.  Now look a circle.  The points in the center (horizontally) are either at the top or bottom, and the points in the middle (vertically) are either on the left or the right.  SO...the natural thing to do with this new revelation is to make a circle.  Open up 'Math - Trigonometry 3.'  The first circle is obviously incomplete, but look at the code (it's the first effect).  What we're doing is stretching i around a circle, but remember, i only goes from 0 to 1.  That would be the radius of the circle, and the perimeter is pi*2*r.  From what I've heard and observed, you can calculate pi (instead of having to type out the number) as the arccosine of -1.  So we need to change the code a bit by multiplying i by 2*pi (or tpi (two pi), which is defined in the init block).  Yay, we've made ourselves a circle!  Isn't that dandy.
The last thing that we're going to explain here is rotation.  I could just give you the code but that wouldn't be fun.  Let's look at it:  If we're rotation at a speed t, we want x to be 1 or -1 when y is 0, and vice versa.  Check out 'Math - Rotation 1.'  This is a superscope with the code:
Frame:
t=t-0.01
Pixel:
x=i*sin(t);
y=i*cos(t);
Well, it's rotating, isn't it?  Yes, but it's only rotating a one-dimensional object.  Sure, you could make a rotating VU meter, but that's boring.  So why not just add v to both x and y?  Because then it only renders right when it's at the top left or bottom right (try it if you want); that's because you're making a diagonal line (I'm speaking of the volume) that only goes one way.  We need to rotate the second dimension as well.  Now open 'Math - Rotation 2.'  The code is now:
...
Pixel:
x=i*sin(t)+v*cos(t);
y=i*cos(t)+v*sin(t);
So why did we switch around the sin and cos?  A problem would arise if we hadn't; we'd be rotating the second dimension in the same position as the first dimension (which is basically the same as drawing a Cartesian map having the x and y axes going the same direction).  But other than that, it should be fine.  Right?  Nope, now it's flipping around.  One more hurdle to jump.  Look at where it's flipping 'backwards.'  It's flipping at the diagonals.  What we have ensures it to be correct when X and Y are either 1 or -1 or 0, but we've neglected up until now to look at where they are in between.  We want these results:  In the first dimension (i), x and y are equal when they are rotated to the top right and bottom left.  In the second dimension (v), we want them to be equal at the top left and bottom right.  If you visualize this in your head it makes sense.  So how do we get these results?  Look at 'Math - Trigonometry 4.'  In the bottom line, the two waves meet between the places where the two waves on the top line meet.  How did we do that?  Now look at the code in the second superscope in the top two groups.  In the second group, y is negated.  Now then, if we apply this code to the second dimension, it should rotate fine.  Open up 'Math - Rotation 3.'  The final, working code is:
...
Pixel:
x=i*sin(t)+v*cos(t);
y=i*cos(t)-v*sin(t);
As you can see, it rotates just fine.

So what can we do with this newfound information?  I would tell you about 3D rotation, but first you need to know how to draw a 3D image on a 2D screen.

3D/2D TRANSLATION

No, not like a Spanish-Klingon translation, a geometrical translation, 'downgrading' from three dimensions to two.  It's the same thing that happens when light hits your retina: all of the light reflecting off surrounding objects hits an essentially two-dimensional plane (yes, I know it's a dome shape, but stay with me).  What happens to objects as they get farther away?  They get smaller, but at a decreasing rate.  This is the same thing that happens in the curve y = 1 / x.  The greater x is the less y is, but as x increases the decrease in y decreases.  Get it?  It follows then that in a 3D room, the greater z (depth) gets, x and y get smaller at a reciprocal rate.  So let's call x1 and y1 the actual x and y coordinates, and x and y the coordinates projected on the screen:
x=x1/z;
y=y1/z;
Take a look at 'Math - 3D-2D 1.'  That's supposed to be a view of a tilted superscope, but it obviously doesn't work.  However, we missed something.  We're trying to make a 3D room that stretches from -1 to 1 on all axes.  That means that in the center, z=0.  Therefore we're dividing by zero (which is possible, don't believe your math teacher - it's just not very pleasant.)  So we need to shift z up by two - so that 0 is in the middle, but it's not 0 at the end; and neither is -1.  In 'Math - 3D-2D 2,' we've fixed the code to:
...
x=x1/(z+2);
y=y1/(z+2);
So what if I make a superscope whose entire detail cannot be seen from one side (think on it a minute)?  That's why I explained rotation before this.  We've seen a rotation around the z axis (the x by y plane), but can we rotate it around any axis?  Why, yes we can!  Just use the axes of the plane that axis is perpendicular to like this:
a=a1*sin(t)+b1*cos(t);
b=a1*cos(t)-b1*sin(t);
where a and b are the axes the plane is on, and a1 and b1 are the coordinates on that axis.  So if we're rotating around the z axis, we use:
x=x1*sin(t)+y1*cos(t);
y=x1*cos(t)-y1*sin(t);
Putting it all together, to rotate a figure around all three axes and project the figure onto the screen:
Frame:
zt=zt+izt; yt=yt+iyt; xt=xt+ixt;
Pixel:
...
x2=x1*sin(zt)+y1*cos(zt); y2=x1*cos(zt)-y1*sin(zt);
x3=x2*sin(yt)+z1*cos(yt); z2=x2*cos(yt)-z1*sin(yt);
y3=y2*sin(xt)+z2*cos(xt); z3=y2*cos(xt)-z2*sin(xt);
x=x3/(z3+2);
y=y3/(z3+2);
Picking it apart again, the ... is where the original coordinates of the scope would be put (as x1, y1, and z1), the next two lines are the rotation around the z axis, the next two are the rotation around the y axis, and the next two are the rotation around the y axis.  Note that we use the most 'current' coordinates in each rotation (the coordinates with the highest number); this is so that when we're rotating the figure, we're rotating it's previous rotation as well.  Also note that I used three separate timers, zt, yt, and xt.  This is to make sure it doesn't always follow the same path.  However, izt, iyt, and ixt should all be randomized regularly, or else they still follow a repetitive path.  The product of this code (applied to a circle) is in 'Math - 3D Rotation 1.'  One last part of this is that the circle has a radius of 1.  However, it's only taking up half of the area it should be.  To project the sphere the ring rotates inside of into the window correctly, shift z4 by sqrt(2) before translating.  To restore the size of the ring correctly, shift z4 by 2 and scale by sqrt(3) before translating.  I.e.:
.../(z4+sqrt(2)); - positions the sphere that the ring rotates inside of from 1 to -1, but when the ring is facing you, it's projected radius is not 1
.../(z4+2)*sqrt(3); - when the ring is facing you, the projected radius is 1, but the sphere is not from 1 to -1.
Both of these effects cannot be achieved at the same time, but I can't explain how very well.

SPEEDUPS

I have seen so much useless, repeated code in AVS that it makes me sick.  I'll discuss some ways to speed up and streamline your coding here.  I know these aren't very big speedups, but they help.  And do note that I come from a c++ background, so if I'm making a stupid, don't blame me, blame that c++ I got in math class last year (ha ha ha...yeah).
Never type commands like x=x.  In superscopes, this gets repeated n times, so it adds up.  In DMs, it gets repeated for every block in the mesh.  (That means w*h times, where w and h are the number of blocks horizontally and vertically, respectively.  So if you have a 25x25 grid, that's 625 times - and if you have a 50x50 grid, that's 2500 times!)  The same goes for this type of code:
a=b;
...
b=a;
OR
a=b
...
c=a;
In the first example, you're just shifting a value between two variables.  If a gets altered between the two commands, just change b accordingly instead.  And in the second example, use:
c=b;
...
because otherwise a becomes a 'deadweight' variable, and the commands under which it is altered become 'deadweight' code.  The ONLY reason to use that line of code is if a is a required coordinate, and c alters other variables later.  Yes, there may be other special instances, but in those cases, just use your head!  If it's really not doing anything but re-routing the variable somewhere else, get rid of it.  Remember, the shortest distance between two points is a straight line, and any side roads the processor has to take slows it down.
Try to avoid using functions where you can use normal operators (+, -, *, /, %) instead.  Such as the function sqr(x).  You can easily use x*x instead, which is faster.  Or even better, the function pow(x,2) can be replaced with either sqr(x) or x*x.  You can simplify any pow function where the second value (the exponent) is a multiple of 0.5:
pow(x,3)=x*x*x;
pow(x,4.5)=x*x*x*x*sqrt(x);
Don't use useless multiplication, either.  By useless multiplication, I mean things like 10*4, or 35/2.  Just calculate them individually to get around the  cumbersome multiplication or division (use 40 and17.5 instead).  However, if you want to be precise, thirds, sixths, sevenths, ninths, et cetera are useful.
Also, try not to calculate the same thing more than once.  For example:
Init:
n=576;
Beat:

Frame:
t=t-0.01;
Point:
x=sin(i*3.14159*2)*cos(t*7+i*3.14159*10)*(1-v/2)/1.5;
y=cos(i*3.14159*2)*sin(t*3+i*3.14159*6)*(1-v/2)/1.5;

could be shortened to
Init:
n=576; tpi=acos(-1)*2;
Beat:

Frame:
t=t-0.01
Point:
r=i*tpi; v1=0.6-v/3;
x=sin(r)*cos(t*7+r*5)*v1;
y=cos(r)*sin(t*3+r*3)*v1;

saving space, and possibly time.
This is useful for the 3D rotations, where, in the case above, you are calculating the sine and cosine of a number twice per pixel.  You only need to calculate that once per frame.  In other words, you can speed it up quite a bit (by relieving 1151 trigonometric calculations per frame!) by using this instead:
...
Frame:
... cz=cos(zt); sz=sin(zt); cy=cos(yt); sy=sin(yt); cx=cos(xt); sx=sin(xt);
Pixel:
x2=x1*sz+y1*cz; y2=x1*cz-y1*sz;
x3=x2*sy+z1*cy; z2=x2*cy-z1*sy;
y3=y2*sx+z2*cx; z3=y2*cx-z2*sx+sqrt(2);
x=x3/z3; y=y3/z3;



There's so much more stuff AVS has to offer, but I can't write it all up here.  So any questions, comments, or revisions can be sent to:
therealatero@hotmail.com



LEGAL STUFF

Copyright (c) 2002 by Stuart Raven
Do not distribute this file without permission from the author.  This file may be used for non-commercial home use only.